Repository navigation
qc: qrypt.chat in the terminal (hqtui client, CLI, MCP, browser login) - #271
Merged
Merged
Conversation
Bare `qc` opens a full-screen end-to-end encrypted chat client on hqtui: chats with unread counts, a bottom-anchored transcript grouped by sender, typing indicators, a live/connecting/offline badge, the OpenEmoji picker on Ctrl+E, :shortcode: emojify on send, and the mouse (one click opens a chat, the wheel scrolls). Scripts get `qc chats|read|send|listen` (--json), agents get `qc mcp` (list_chats, read_chat, send_message). Encryption is the web app's own ML-KEM-1024 code run in Node/Bun with the keys handed in: a message is encrypted per participant here, and the server only ever sees ciphertext. `qc login` is OAuth 2.1 authorization code + PKCE, started from the CLI: - /cli/authorize (web): the signed-in app approves, seals the account keypair to a one-time ML-KEM-1024 key qc generated, and stores a 5-minute single-use code (hash only) in cli_auth_codes. - /api/cli/token: code + verifier -> a fresh Supabase session of qc's own (never the browser's: refresh tokens rotate) + the sealed keys; grant_type=refresh_token rotates. Phone-only/anon accounts get a confirmed synthetic address for the magic-link bridge, like names do. - Loopback redirect, or --oob to paste a code over SSH. Both sides show a confirmation code derived from the challenge and the one-time key. /api/events now authenticates with authenticateRequest (Bearer or cookie), so a CLI can follow the live stream. Migration 20261006120000_cli_auth_codes: RLS on, no policies, service_role only. Applied on dev2. Publishable package: packages/qryptchat (@profullstack/qryptchat, bins qc + qryptchat), built by scripts/build-qc.js. The root `qryptchat` bin and `cli` script now point at qc; the old WebSocket-era blessed CLI is left in place, unused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ThreatCrush Security Scan13 finding(s) MEDIUM: 9 | LOW: 4
Snippets are redacted; ThreatCrush never prints matched credential material. |
Contributor
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
qc, qrypt.chat's terminal client. The CLI that was there before couldn't do any of this: it talked to a WebSocket server the Next app no longer runs, and it sent plaintext.What you get
qcopens a full-screen chat client built on hqtui 0.8.0::rocket:-style shortcodes turned into emoji on sendqc chats | read <chat> | send <chat> <text|-> | listenfor scripts, with--json.qc mcpruns an MCP server on stdio with the toolslist_chats,read_chatandsend_message.Encryption
post-quantum-encryption.js), run in Node/Bun with the keys passed in.qc login: OAuth 2.1, authorization code + PKCE, started from the CLI/cli/authorize. The request carries an S256 challenge and a one-time ML-KEM-1024 public key, and qc prints a confirmation code.exportUserKeys) to the one-time key.POST /api/cli/authorizethen stores a 5-minute, single-use code incli_auth_codes; only the code's hash is stored.POST /api/cli/tokenwith the code and verifier. It gets back its own Supabase session, minted viagenerateLink→verifyOtp, plus the sealed keys.grant_type=refresh_tokenrotates the session.--oobshows the code in the browser for you to paste into the terminal.Server changes
/api/eventsnow usesauthenticateRequest, so it accepts a Bearer token or the cookie. A CLI can now follow the live stream.20261006120000_cli_auth_codes: RLS on, no policies, service_role only. It is already applied on dev2, and I verifiedanon/authenticatedhave no access andservice_rolecan insert.Package
packages/qryptchat→@profullstack/qryptchaton npm, with binsqcandqryptchat.bun scripts/build-qc.jsbundlessrc/cliand the crypto it uses into about 70 KB, with the TUI and MCP as lazy chunks. The runtime dependencies are@profullstack/hqtui,mlkemand@noble/ciphers.public/skill.mdandllms.txtdescribe qc.skill.mdused to say there was no API.Tests
24 new tests in
tests/cli/:renderToScreen, including the picker, a click that opens a chat, and wheel scrollResults:
bun run test:ci: 593/593.next buildpasses when Supabase env is set; without it, it fails at/api/auth/upload-avatarexactly as it did before this change.Not in this PR
/api/messages/loadsorts ascending and then applies the limit, so a chat with more than 100 messages shows its first 100, not its latest. The web app has the same behaviour. This is Preshy's messaging area, so I left it alone.bin/qryptchat-cli.js(blessed/WebSocket) is still in the repo but nothing points at it any more.🤖 Generated with Claude Code